﻿using UnityEngine;
using System.Collections;

/**TODO DOCUMENTAZIONE OBSOLETA
 * \brief Questo script permette al GameObject che lo detiene di avere l'abilita di muoversi nell'ambiente 2D
 * 
 * Il detentore di questo script avrà la possibilità di muoversi attraverso il motore fisico di Unity lungo
 * il piano XY di unity.
 * 
 * Le condizioni di utilizzo di questo componente sono le seguenti:
 * \li il gameobject detentore possiede anche un rigidbody2D;
 * \li l'input dell'utente con le WASD è sempre compreso tra [-1;+1];
 * \li maxForce deve avere componenti tutte positive;
 * \li intensityCoefficient deve avere componenti positive;
 * 
 * TODOs:
 * \li il prodotto scalare currentDirection . previousdireciton non funziona perché l'aggiornamento previousdirection con velocity non si comporta come aspettato
 * 
 * \author Koldar
 * \date \today
 * \version 1.0
 */
[RequireComponent  (typeof(Rigidbody2D)) ]
public class InputPhysicMoveXYHelper : MonoBehaviour {

	private static readonly Vector2 NO_MOVEMENT=new Vector2(0f,0f);

	/**Indica la forza massima che l'utente può applicare ad un qualunque movimento sull'utente. Per esempio
	 * se la forza massima è (50,50) l'utente potrà applicare al masismo (50,50) di forza quando tiene premuto per
	 * un certo lasso di tempo il tasto di moviemnto a sinistra.
	 */
	public Vector2 maxForce;
	/**Indica il fattore di crescita dell'intensità della forza. Per esempio se esso vale (1,1) e la forza applicata al momento
	 * è (4,5) dopo un secondo la forza sarà (5,6)
	 */
	public Vector2 IntensityCoefficient;
	/**\brief indica l'orientazione del mondo.
	 * 
	 * \li above indica che la telecamera guarda dallo zenit del giocatore. Non è dunque presente la gravità 
	 * dal punto di ivista della camera;
	 * \li lateral indica che la telecamera è su un piano diverso da quello del giocatore. E' dunque presente
	 * la gravità. Inoltre i movimenti accettati sono solo destra-sinistra (ma niente alto-basso)
	 */
	public WorldOrientation orientation;

	/**Indica la direzione che il corpo aveva nel rame calcolato prima di questo
	 */
	protected Vector2 previousDirection;
	/**Indica la direzione che l'utente vorrebbe dare al gameobject con i tasti WASD.
	 * le componenti di questo vettore possono valere solo {-1;0;+1}
	 */ 
	protected Vector2 currentDirection;
	/**Indica l'intensità della forza da applicare al gameobject in questo esatto momento.
	 * Le componenti di questo vettore hanno valore sempre >=0 ma sempre minore di quelle in maxForce
	 */
	protected Vector2 currentForceIntensity;
	/**Indica la forza da applicare al gameobject in questo esatto momento. E' la combinazione
	 * di currentDirection con currentForceIntensity
	 */ 
	protected Vector2 currentForce;

	private void Start () {
		this.currentForceIntensity=NO_MOVEMENT;
		this.previousDirection=NO_MOVEMENT;
		this.currentDirection=NO_MOVEMENT;
	}

	public virtual void requestMovement(Vector2 _movement){
		this.currentDirection=_movement;
	}

	private void Update () {
		this.currentDirection=new Vector2(Input.GetAxis("Horizontal"),Input.GetAxis("Vertical"));
		//*********************** DIREZIONE DELLA FORZA DA APPLICARE ************************

		if (this.orientation==WorldOrientation.lateral){
			this.currentDirection.y=0;
		}

		//*********************** INSTENSITA' DELLA FORZA DA APPLICARE *************************

		//ora devo simulare l'accelerazione/decelerazione del gameobject: esso accelera se l'utente decide una direzione che è concorde
		//con la sua velocità, decelera se la direzione voluta è discorde con la sua velocità oppure se non sta premendo alcunché.
		//per capire se 2 vettori (a mo di rosa dei venti) sono concordi o discordi possiamo usare il prodotto scalare: se esce + allora
		//sono corcordi, se esce - allora sono discordi.
		//Debug.Log (this.currentDirection.ToString()+" "+this.previousDirection.ToString()+" dot: "+Vector2.Dot(this.currentDirection,this.previousDirection));
		if (Vector2.Dot(this.currentDirection,this.previousDirection)<0 || this.currentDirection==NO_MOVEMENT){
			//bisogna decelerare, quindi andiamo a gestire la currentForceIntensity
			//Debug.Log ("decelero: "+this.currentForceIntensity.ToString());
			if (this.currentForceIntensity!=NO_MOVEMENT){
				var tmpx=this.currentForceIntensity.x;
				var tmpy=this.currentForceIntensity.y;
				
				//faccio in modo che ogni componente non possa mai superare la corrispeettiva (0,0) (aka NO_MOVEMENT)
				tmpx=tmpx-Time.deltaTime*this.IntensityCoefficient.x<NO_MOVEMENT.x?
				      NO_MOVEMENT.x:
				      tmpx-Time.deltaTime*this.IntensityCoefficient.x;

				tmpy=tmpy-Time.deltaTime*this.IntensityCoefficient.y<NO_MOVEMENT.x?
				      NO_MOVEMENT.y:
				      tmpy-Time.deltaTime*this.IntensityCoefficient.y;

				this.currentForceIntensity.Set(
					tmpx,
					tmpy
				);
			}

		}
		if (this.currentDirection!=NO_MOVEMENT){
			//Debug.Log ("accelero: "+this.currentForceIntensity.ToString());
			//bisogna accelerare, quindi andiamo a gestire la currentForceIntensity
			//calcolo le costanti di amplificazione dell'intensità della forza da applicare
			//dato che non esiste l'operatore <= per i vettori, devo fare in modo che ogni componente di currentForceIntensity non possa MAI superare quelle
			//di maxForce. In questo modo il != diventa per necessità "almeno una componente di currentForceIntensity è strettamente minore della corrispettiva
			//in maxForce
			if (this.currentForceIntensity!=this.maxForce){
				
				var tmpx=this.currentForceIntensity.x;
				var tmpy=this.currentForceIntensity.y;
				
				//faccio in modo che ogni componente non possa mai superare la corrispeettiva maxForce
				tmpx=tmpx+Time.deltaTime*this.IntensityCoefficient.x>this.maxForce.x?
					this.maxForce.x:
					tmpx+Time.deltaTime*this.IntensityCoefficient.x;
				tmpy=tmpy+Time.deltaTime*this.IntensityCoefficient.y>this.maxForce.y?
					this.maxForce.y:
					tmpy+Time.deltaTime*this.IntensityCoefficient.y;

				this.currentForceIntensity.Set(
					tmpx,
					tmpy
				);
			}
		}

		//************************** CALCOLO DELLA FORZA DA APPLICARE ******************************
		//unisco le 2 informazioni precedentemente ricavate

		this.currentForce.Set(this.currentDirection.x,this.currentDirection.y);
		this.currentForce.Scale(this.currentForceIntensity);
	}

	private void FixedUpdate(){
		this.rigidbody2D.AddForce(this.currentForce);
		this.previousDirection.Set(this.rigidbody2D.velocity.x,this.rigidbody2D.velocity.y);
	}

	public Vector2 getCurrentDirection(){
		return new Vector2(this.currentDirection.x,this.currentDirection.y);
	}

}
